#include <Esp.h>
#include <ESP8266WiFi.h>
#include <esp8266_peri.h>
#include <WiFiUdp.h>
#include "ezTime.h"
#include "font.h"

//#define USE_SERIAL 1

#ifdef USE_SERIAL
#define SERIAL_INIT Serial.begin(115200)
#define _print(a) Serial.print(a)
#define _println(a) Serial.println(a)
#else
void dummy(){}
#define SERIAL_INIT dummy()
#define _print(a) dummy()
#define _println(a) dummy()
#endif

void hw_wdt_disable(){
  *((volatile uint32_t*) 0x60000900) &= ~(1); // Hardware WDT OFF
}

void hw_wdt_enable(){
  *((volatile uint32_t*) 0x60000900) |= 1; // Hardware WDT ON
}
#define REG_WDT_BASE 0x60000900
#define WDT_CTL (REG_WDT_BASE + 0x0)
#define WDT_CTL_ENABLE (BIT(0))


char ssid[] = "zsh1";     // your network SSID (name)
char password[] = "92066200"; // your network key


#define BEEP_HI GPOS = (1 << 1)
#define BEEP_LO GPOC = (1 << 1)

#define CLK_HI GPOS = (1 << 14)
#define CLK_LO GPOC = (1 << 14)
#define LAT_HI GP16O |= 1
#define LAT_LO GP16O &= ~1
#define OE_HI GPOS = (1 << 2)
#define OE_LO GPOC = (1 << 2)
#define D0_HI GPOS = (1 << 13)
#define D0_LO GPOC = (1 << 13)
#define A 5
#define B 4
#define C 15
#define D 12
/*
nodemcu0.9 ESP12-module

gpio13 dat
x
x
x
gpio05 a  gpio04 b
gpio15 c  gpio12 d
gpio14 clk  gpio16 lat  
gpio02 oe

13 beep  800hz  1600hz 0.25s  0.5s
  获取而未同步， 红色显示当前时间， 绿色显示标准时间


rst                                1 txd
adc                                3 rxd
ch_pd                              5
16                                 4
14                                 0
12                                 2
13                                 15
VCC                                GND

     cs0  miso  xx9  xx10  mosk  sck 
   
*/

uint32_t cR;
uint32_t cG;
uint32_t cB;

uint32_t frameBuffer[3][64];
void setPixel(int x, int y, int val){
  int part = y>>4 & 1;
  int pix = (y&15) * 64 + x;
  pix = pix * 2 + part;
  uint32_t v;
  
  v = frameBuffer[2][pix/32];
  v = v & ~(1<<(pix&31));
  if(val && cR){
    v = v | (1<<(pix&31));
  }
  frameBuffer[2][pix/32] = v;

  v = frameBuffer[1][pix/32];
  v = v & ~(1<<(pix&31));
  if(val && cG){
    v = v | (1<<(pix&31));
  }
  frameBuffer[1][pix/32] = v;

  v = frameBuffer[0][pix/32];
  v = v & ~(1<<(pix&31));
  if(val && cB){
    v = v | (1<<(pix&31));
  }
  frameBuffer[0][pix/32] = v;
}

void drawChar(int16_t x, int16_t y, unsigned char c) {
  if (c >= 176) c++; // Handle 'classic' charset behavior
  int8_t i;
  for (int8_t j = 0; j < 8; j++) {
    setPixel(x, y + j, 0);
  }
  for (i = 0; i < 5; i++) { // Char bitmap = 5 columns
    uint8_t line = pgm_read_byte(&font[c * 5 + i]);
    for (int8_t j = 0; j < 8; j++, line >>= 1) {
      setPixel(x + i + 1, y + j, line & 1);
    }
  }
}

void drawChar16(int16_t x, int16_t y, unsigned char c) {
  for(int j = 0; j<16;j++){
    int pos = ((int)c) * 16;
    int d = pgm_read_byte(&font16[pos + j]);
    for(int i = 0; i<8;i++){
      if (d & 0x80){
        setPixel(x + i, y + j, 1);
      }else{
        setPixel(x + i, y + j, 0);
      }
      d = d << 1;
    }
  }
}

void drawChar16Test(int16_t x, int16_t y, unsigned char c) {
  _print("\r\n");
  for(int j = 0; j<16;j++){
    int pos = ((int)c) * 16;
    int d = pgm_read_byte(&font16[pos + j]);
    for(int i = 0; i<8;i++){
      if (d & 0x80){
        //setPixel(x + i, y + j, 1);
        _print("[]");
      }else{
        //setPixel(x + i, y + j, 0);
        _print("__");
      }
      d = d << 1;
    }
    _print("\r\n");
  }
}

Timezone localTime;
Timezone myTZ;
Timezone* showTz;

void setup(void) {
  // disable hardware watchdog
  CLEAR_PERI_REG_MASK(WDT_CTL, WDT_CTL_ENABLE);
  hw_wdt_disable();
  ESP.wdtDisable();
  SERIAL_INIT;

  // for(int i=0;i<256;i++){
  //   _println(i);
  //   drawChar16Test(0, 0, i);
  // }
  // while(1);

  pinMode(14, OUTPUT);
  pinMode(16, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(15, OUTPUT);
  pinMode(12, OUTPUT);
#ifdef USE_SERIAL
#else
  pinMode(3, INPUT);//rxd
  pinMode(1, INPUT);//txd
#endif
  pinMode(0, INPUT);//boot mode
  digitalWrite(2, 1);
  showTz = &myTZ;

  CLEAR_PERI_REG_MASK(WDT_CTL, WDT_CTL_ENABLE);
  hw_wdt_disable();
  ESP.wdtDisable();
}

#define INIT        0
#define GET_TIME    1
#define WIFI_WAIT   2
#define NTP_WAIT    3
#define NTP_TIMEOUT 4
#define IDLE        5

WiFiUDP udp;
int status = INIT;
int count;
int ntp_timeout;
int ntp_fail_count;
int bright = 1;
int ntp_server_index = 0;
int beepTime = 0;
int lastHr = -1;
int lastMin = -1;
void proc(){

  // int cc = Serial.read();
  // int needSync = 0;

  // if(cc=='a'){
  //   showTz = &myTZ;
  //   _print("myTZ:");
  // }
  // if(cc=='b'){
  //   showTz = &localTime;
  //   _print("localTime:");
  // }
  // if(cc=='c'){
  //   localTime._last_sync_time = myTZ._last_sync_time;
  //   //localTime._last_sync_millis = myTZ._last_sync_millis;
  //   localTime._last_sync_micros = myTZ._last_sync_micros;
  //   _print("sync:");
  // }
  // if(cc=='d'){
  //   needSync = 1;
  //   _print("needSync:");
  // }

  // if(cc=='e'){
  //   uint64_t m64 = micros64();
  //   _print("micros64:");
  //   _print(m64);
  //   _print(",");
  // }

  if(!digitalRead(0)){//boot mode press down
    //sync
    localTime._last_sync_time = myTZ._last_sync_time;
    //localTime._last_sync_millis = myTZ._last_sync_millis;
    localTime._last_sync_micros = myTZ._last_sync_micros;
  }

  if(!digitalRead(3)){//boot mode press down
    cR = 0;
    cG = 1;
    cB = 0;
    showTz = &myTZ;
  }else{
    cR = 1;
    cG = 1;
    cB = 1;
    showTz = &localTime;
  }

  _print("status:");
  _print(status);
  _print("  ");

  tmElements_t tm;
  showTz->breakTime(tm);

  int mon = tm.Month;
  int day = tm.Day;
  int wd = tm.Wday;
  int sec = tm.Second;
  int min = tm.Minute;
  int hr = tm.Hour;
  
//  if(!digitalRead(3)){//boot mode press down
//  min = 59;
//  sec = 59;
//  hr = 7;
//  }

  if((lastMin != min || lastHr != hr) && (min==59 || min == 29)){
    if(sec == 58 || sec == 59){
      lastHr = hr;
      lastMin = min;
      int time;

      if(hr==22){
        time = 0;
      }else if(hr==23){
        time = 0;
      }else if(hr==0){
        time = 0;
      }else if(hr==1){
        time = 0;
      }else if(hr==2){
        time = 0;
      }else if(hr==3){
        time = 0;
      }else if(hr==4){
        time = 0;
      }else if(hr==5){
        time = 0;
      }else if(hr<12){
        time = hr + 1;
      }else{
        time = hr - 12 + 1;
      }

      if(time!=0 && min==29){
        time = 1;
      }
      beepTime = time;
    }
  }

  if(beepTime>0){
      pinMode(1, OUTPUT);//txd

      if(beepTime==1){
        for(int i=0;i<800;i++){//0.5s
          BEEP_HI;
          delayMicroseconds(312);
          BEEP_LO;
          delayMicroseconds(312);
        }
      }else{
        //800hz
        for(int i=0;i<200;i++){//0.25s
          BEEP_HI;
          delayMicroseconds(625);
          BEEP_LO;
          delayMicroseconds(625);
        }
      }

      pinMode(1, INPUT);//txd
      beepTime--;
  }


  // 01234567
  // hh:mm:ss
  // MM/dd w S
  // S:
  // w: wifi
  // n: ntp
  // 1: ntp_fail_count

  int wd0 = (wd-1);
  if(wd0==0) wd0 = 7;

  char mon0 = '0'+(mon/10);
  char mon1 = '0'+(mon%10);

  char day0 = '0'+(day/10);
  char day1 = '0'+(day%10);

  char hr0 = '0'+(hr/10);
  char hr1 = '0'+(hr%10);

  char min0 = '0'+(min/10);
  char min1 = '0'+(min%10);

  char sec0 = '0'+(sec/10);
  char sec1 = '0'+(sec%10);

  _print((char)('0'+wd0));_print(",");
  _print(mon0);_print(mon1);_print("/");

  _print(day0);_print(day1);
  _print(" ");
  _print(hr0);_print(hr1);_print(":");
  _print(min0);_print(min1);_print(":");
  _print(sec0);_print(sec1);

  _print("  ");
  _print(localTime._last_sync_time);_print(", ");
  //_print(localTime._last_sync_millis);_print(", ");
  _print(localTime._last_sync_micros);_print(",   ");
  _print(myTZ._last_sync_time);_print(", ");
  //_print(myTZ._last_sync_millis);_print(", ");
  _print(myTZ._last_sync_micros);_print(", ");
  _println();

  int val = analogRead(0);
  //_print("val:");_println(val);
  bright = val/50;
  if(bright>20)bright = 20;
  if(bright<1)bright=1;

  for(int i=0;i<64;i++){
    frameBuffer[0][i] = 0;
    frameBuffer[1][i] = 0;
    frameBuffer[2][i] = 0;
  }
  int x = 0;
  if(hr0=='0'){
    x+=4;
  }else{
    drawChar16(x, 0, hr0); x+=8;
  }
  drawChar16(x, 0, hr1); x+=8;
  drawChar16(x, 0, ':'); x+=8;
  drawChar16(x, 0, min0); x+=8;
  drawChar16(x, 0, min1); x+=8;
  drawChar16(x, 0, ':'); x+=8;
  drawChar16(x, 0, sec0); x+=8;
  drawChar16(x, 0, sec1); x+=8;
  x = 0;
  
  if(mon0=='0'){
    x+=4;
  }else{
    drawChar16(x, 16, mon0); x+=8;
  }
  drawChar16(x, 16, mon1); x+=8;
  drawChar16(x, 16, '/'); x+=8;
  if(day0=='0'){
  drawChar16(x, 16, day1); x+=8;
                           x+=8;
  }else{
    drawChar16(x, 16, day0); x+=8;
  drawChar16(x, 16, day1); x+=8;
  }
  //x+=4;
  drawChar16(x, 16, 242 + (wd0-1)*2); x+=8;//242=一
  drawChar16(x, 16, 243 + (wd0-1)*2); x+=8;//242=一
  //x+=4;

  switch(status){
    case INIT: {
      count = 36000;
      ntp_fail_count = 0;
      ntp_server_index = 0;
      _println();
      _println();
      day = 0;

      status = GET_TIME;
      break;
    }
    case GET_TIME: {
      _print("Connecting Wifi: ");
      _println(ssid);
      WiFi.mode(WIFI_STA);
      WiFi.begin(ssid, password);
      status = WIFI_WAIT;
      drawChar16(x, 16, 'w');
      break;
    }
    case WIFI_WAIT: {
      if (WiFi.status() == WL_CONNECTED) {
        _println("WiFi connected");
        _print("IP address: ");
        _println(WiFi.localIP());

        switch(ntp_server_index){
          case 0: myTZ.req("pool.ntp.org", &udp);
          case 1: myTZ.req("time.windows.com", &udp);
          case 2: myTZ.req("time.nist.gov", &udp);
        }
        drawChar16(x, 16, 'n');
        
        ntp_timeout = 10;
        status = NTP_WAIT;
      }else{
        drawChar16(x, 16, 'w');
      }
      break;
    }
    case NTP_WAIT: {
      if(myTZ.isRes(&udp)){
        time_t t;
        bool ret = myTZ.res(&udp, t);
        if(ret){
          myTZ.setTime1(t);
        }
        _println("done, wifi off.");
        WiFi.mode(WIFI_OFF);
        ntp_fail_count = 0;
        status = IDLE;
      }else{
        drawChar16(x, 16, 'n');
        ntp_timeout--;
        if(ntp_timeout!=0){
          _print("ntp wait:");
          _println(ntp_timeout);
        }else{
          status = NTP_TIMEOUT;
          WiFi.mode(WIFI_OFF);
        }
      }
      break;
    }
    case NTP_TIMEOUT: {
      ntp_server_index++;
      if(ntp_server_index==3){
        ntp_server_index = 0;
      }
      if(ntp_fail_count<9){
        ntp_fail_count++;
      }
      drawChar16(x, 16, 'n');
      status = IDLE;
      break;
    }
    case IDLE: {
      count--;
      if(ntp_fail_count>0){
        drawChar16(x, 16, '0'+ntp_fail_count);
      }
      if(count == 0){
        status = GET_TIME;
        count = 36000;
      }
      break;
    }
    default: break;
  }
  
  cR = 0;
  cG = 1;
  cB = 0;
  setPixel(0, 0, 1);
}

int lastmills;
void loop(void) {

  int t = millis();
  int diff = lastmills-t;
  if(diff>1000 || diff < -1000){
    lastmills = t;
    proc();
  }
  for(uint8_t f = 0; f<16;f++){
    digitalWrite(A, f&1);
    digitalWrite(B, f&2);
    digitalWrite(C, f&4);
    digitalWrite(D, f&8);

    for(uint8_t k = 0; k < 3; k++){//three color
      for(uint8_t j = 0; j < 4; j++){
        uint32_t v = frameBuffer[k][(f<<2)+j];
        for(uint8_t i = 0; i < 16; i++){
          v=v>>1;
          if(v & 1){
            D0_HI;
          }else{
            D0_LO;
          }
          v=v>>1;
          CLK_HI;
          CLK_LO;

        }
      }
      for(uint8_t j = 0; j < 4; j++){
        uint32_t v = frameBuffer[k][(f<<2)+j];
        for(uint8_t i = 0; i < 16; i++){
          if(v & 1){
            D0_HI;
          }else{
            D0_LO;
          }
          v=v>>1;
          v=v>>1;
          CLK_HI;
          CLK_LO;
        }
      }
    }
  
    LAT_HI;
    LAT_LO;

    OE_LO;
    delayMicroseconds(bright);
    OE_HI;
  }
  delay(16);
}
